#![allow(non_snake_case)]

use crate::{
    use_message, DatePicker, ExpandMode, Input, MessageVariant, MultiTreeSelect, RadioGroup,
    RadioItem, TreeItems,
};
use chrono::NaiveDate;
use icondata as i;
use leptos::{ev, prelude::*};
use leptos_icons::*;
use shq_common::prelude::{AndOr, DataType, Direction, Operator, WhereCause};
use thaw_utils::{ArcCallback, ArcOneCallback, ArcTwoCallback};
#[component]
pub(super) fn ColumnOrder(
    order_signal: RwSignal<Option<Direction>>,
    #[prop(into)] sel_col: WriteSignal<Option<(String, bool)>>,
) -> impl IntoView {
    view! {
        <div class="divider">排序</div>
        <div class="join justify-center">
            <button class="btn join-item" class=("btn-active",move||order_signal.get()==Some(Direction::Asc))
                on:click=move|_|{
                    order_signal.set(Some(Direction::Asc));
                    sel_col.update(|col|{ col.as_mut().map(|(_, show)|*show=true); });
                }
            >
                <Icon icon=i::BsSortDownAlt/>升序
            </button>
            <button class="btn join-item" class=("btn-active",move||order_signal.get()==Some(Direction::Desc))
                on:click=move|_|{
                    order_signal.set(Some(Direction::Desc));
                    sel_col.update(|col|{ col.as_mut().map(|(_, show)|*show=true); });
                }
            >
                <Icon icon=i::BsSortDown/>降序
            </button>
            <button class="btn join-item" class=("btn-active",move||order_signal.get().is_none())
                on:click=move|_|{
                    order_signal.set(None);
                    sel_col.update(|col|{ col.as_mut().map(|(_, show)|*show=true); });
                }
            >
                <Icon icon=i::ChChevronsUpDown/>不排序
            </button>
        </div>
    }.into_any()
}

#[component]
pub(super) fn ColumnFilter(
    #[prop(into)] column_name: Oco<'static, str>,
    #[prop(into)] data_type: DataType,
    #[prop(optional, into)] can_null: bool,
    #[prop(into)] filters: RwSignal<Option<WhereCause>>,
    #[prop(into)] tree_items: Option<StoredValue<TreeItems>>,
    #[prop(into)] sel_col: WriteSignal<Option<(String, bool)>>,
    #[prop(into)] page: WriteSignal<usize>,
) -> impl IntoView {
    let message = use_message();
    let opts_blank = tree_items.is_none();
    let is_option_kind = RwSignal::new({
        let mut option_kind = if opts_blank { false } else { true };
        filters.with_untracked(|fs| {
            if let Some(f) = fs.as_ref() {
                if let Some(oper) = f.filter_items.get(0) {
                    option_kind = oper.operator == Operator::In;
                }
            }
        });
        option_kind
    });
    let default_oper = move || {
        if is_option_kind.get_untracked() {
            return Operator::In;
        }
        match data_type {
            DataType::Bool => Operator::Eq,
            DataType::Int => Operator::Gte,
            DataType::Float => Operator::Gte,
            DataType::String => Operator::Like,
            DataType::Date => Operator::Gte,
            DataType::Datetime => Operator::Gte,
        }
    };
    let filter_items = RwSignal::new(filters.with_untracked(|fs| {
        let mut state: Vec<(Operator, Option<String>)> = fs
            .as_ref()
            .map(|f| {
                f.filter_items
                    .iter()
                    .map(|fi| (fi.operator.clone(), fi.value.clone()))
                    .collect()
            })
            .unwrap_or_default();
        if state.is_empty() {
            state = vec![(default_oper(), None)];
        }
        state
    }));
    Effect::new(move || {
        filter_items.update(|fis| {
            fis[0].0 = default_oper();
        })
    });
    let init_and_or = move || {
        let and_or = filters.with_untracked(|fs| fs.as_ref().map(|f| f.and_or.to_string()));
        if and_or.is_none() {
            return Some("and".to_string());
        } else {
            and_or
        }
    };
    let and_or: RwSignal<Option<String>> = RwSignal::new(init_and_or());
    let column_name = StoredValue::new(column_name.to_string());
    let on_operator_change = ArcTwoCallback::new(move |index: usize, oper: Operator| {
        filter_items.update(|fis| {
            fis[index].0 = oper;
        });
    });
    let on_value_change = ArcTwoCallback::new(move |index: usize, value: Option<String>| {
        filter_items.update(|fis| {
            fis[index].1 = value;
        });
    });
    let get_filter_controls = move || {
        let on_operator_change = on_operator_change.clone();
        let on_value_change = on_value_change.clone();
        if is_option_kind.get() && tree_items.is_some() {
            let tree_items = tree_items.unwrap();
            let sel_vals = filter_items.with_untracked(|fis| {
                let v = fis[0].1.clone();
                if let Some(v) = v {
                    v.split(',').map(|s| s.to_string()).collect()
                } else {
                    vec![]
                }
            });
            let on_change = move |vals: Vec<String>| {
                filter_items.update(|fis| {
                    let value = &mut fis[0].1;
                    if !vals.is_empty() {
                        *value = Some(vals.join(","));
                    } else {
                        *value = None;
                    }
                });
            };
            either_of::Either::Left(view! {
                <MultiTreeSelect values=sel_vals tree_items on_change height=300 can_filter=false init_expand_mode=ExpandMode::First show_confirm_cancel_btn=false/>
            })
        } else {
            either_of::Either::Right(view! {<For
                each=move || filter_items.with(|fis|fis.to_vec()).into_iter().enumerate()
                key=|(i,fi)| (*i,fi.0)
                let:((i,(oper, value)))
            >
                <FilterItem column_name=column_name dtype=data_type oper value index=i can_null
                    can_add=MaybeProp::derive(move || Some(filter_items.with(|fis| fis.len() == i + 1)))
                    can_del=MaybeProp::derive(move || Some(filter_items.with(|fis| fis.len() > 1 && i > 0)))
                    on_operator_change=on_operator_change.clone()
                    on_value_change=on_value_change.clone()
                    on_add_item=ArcCallback::new(move || {
                        filter_items.update(|fis| {
                            fis.push((default_oper(), None));
                        });
                    })
                    on_del_item=ArcOneCallback::new(move |index: usize| {
                        filter_items.update(|fis| {
                            if fis.len() > 1 {
                                fis.remove(index);
                            } else {
                                fis[0] = (default_oper(), None);
                            }
                        });
                    })
                    />
            </For>})
        }
    };
    let verify_value = move |oper: Operator, val: &str| {
        let dt = data_type;
        if dt == DataType::Date || dt == DataType::Datetime {
            let format = if dt == DataType::Date {
                "%Y%m%d"
            } else {
                "%Y%m%d %H%M%S" // 日期时间包含时分秒
            };
            match oper {
                Operator::Eq
                | Operator::NotEq
                | Operator::Gt
                | Operator::Gte
                | Operator::Lt
                | Operator::Lte => match NaiveDate::parse_from_str(val, format) {
                    Ok(_) => true,
                    Err(_) => {
                        message.create(
                            format!("{}不是有效的日期，正确的格式如[20200101]", val),
                            MessageVariant::Error,
                            Default::default(),
                        );
                        false
                    }
                },
                _ => true,
            }
        } else {
            true
        }
    };
    let filter_kind_view = move || {
        if !opts_blank {
            Some(view! {<div class="join justify-center">
                <button class="btn btn-sm join-item" class=("btn-active",move||is_option_kind.get())
                    on:click=move|_|{
                        is_option_kind.set(true);
                    }
                >
                    <Icon icon=i::IoOptionsOutline/>从选项中选择
                </button>
                <button class="btn btn-sm join-item" class=("btn-active",move||!is_option_kind.get())
                    on:click=move|_|{
                        is_option_kind.set(false);
                    }
                >
                    <Icon icon=i::RiInputMethodDesignLine/>按输入值筛选
                </button>
            </div>})
        } else {
            None
        }
    };
    let and_or_view = move || {
        if filter_items.with(|fis| fis.len() > 1) {
            Some(view! {
                <RadioGroup name="and_or" value=and_or>
                    <RadioItem key="and">
                        "并且"
                    </RadioItem>
                    <RadioItem key="or">
                        "或者"
                    </RadioItem>
                </RadioGroup>
            })
        } else {
            None
        }
    };
    view! {
        <div class="divider">筛选</div>
        {filter_kind_view}
        {get_filter_controls}
        {and_or_view}
        <div class="flex flex-row gap-2 justify-center">
            <button class="btn" on:click=move|_|{
                let is_black=filter_items.with(|fis|fis.iter().any(|(_, v)| v.is_none() || v.as_ref().map(|s| s.is_empty()).unwrap_or(false)));
                if is_black {
                    message.create(
                        "请输入值".to_string(),
                        MessageVariant::Error,
                        Default::default(),
                    );
                    return;
                }
                let and_or=if let Some(sval)=and_or.get() {
                    if sval.as_str()=="and" {
                        AndOr::And
                    }else{
                        AndOr::Or
                    }
                }else{
                    AndOr::And
                };
                let mut fvals=WhereCause::new(column_name.get_value(),and_or);
                filter_items.with(|fis|fis.iter().for_each(|(oper, value)| {
                    if let Some(val) = value {
                        if !verify_value(oper.clone(), val.as_str()) {
                            return;
                        }
                        let v=val.clone();
                        fvals.add_filter_item(oper.clone(), Some(v));
                    }
                }));
                filters.set(Some(fvals));
                sel_col.update(|col|{ col.as_mut().map(|(_, show)|*show=false); });
                page.set(1);
            }>
                <Icon icon=i::AiCheckCircleOutlined/>确定
            </button>
            <button class="btn"
                    on:click=move|_|{
                        filters.set(None);
                        sel_col.update(|col|{ col.as_mut().map(|(_, show)|*show=false); });
                    }
                >
                <Icon icon=i::AiCloseCircleOutlined/>删除
            </button>
            <button class="btn" on:click=move|_|{
                sel_col.update(|col|{ col.as_mut().map(|(_, show)|*show=false); });
            }>
                <Icon icon=i::AiCheckCircleOutlined/>关闭
            </button>
        </div>
    }
    .into_any()
}

#[component]
fn FilterItem(
    column_name: StoredValue<String>,
    dtype: DataType,
    index: usize,
    oper: Operator,
    value: Option<String>,
    can_null: bool,
    #[prop(into)] can_add: MaybeProp<bool>,
    #[prop(into)] can_del: MaybeProp<bool>,
    on_operator_change: ArcTwoCallback<usize, Operator>,
    on_value_change: ArcTwoCallback<usize, Option<String>>,
    on_add_item: ArcCallback,
    on_del_item: ArcOneCallback<usize>,
) -> impl IntoView {
    let value = StoredValue::new(value);
    let mut opers = vec![Operator::Eq, Operator::NotEq];
    if dtype != DataType::Bool {
        opers.extend(vec![
            Operator::Gt,
            Operator::Gte,
            Operator::Lt,
            Operator::Lte,
            Operator::Like,
            Operator::NotLike,
            Operator::LeftLike,
            Operator::NotLeftLike,
            Operator::RightLike,
            Operator::NotRightLike,
        ]);
    }
    if can_null {
        opers.extend(vec![Operator::Null, Operator::NotNull]);
    }
    let control_view = move || match dtype {
        DataType::Bool => either_of::EitherOf3::A(view! {
            <RadioGroup name=column_name.get_value() value=value.get_value()>
                <RadioItem key="1">
                    "是"
                </RadioItem>
                <RadioItem key="0">
                    "否"
                </RadioItem>
            </RadioGroup>
        }),
        DataType::Date | DataType::Datetime => {
            let value = value.with_value(|val| {
                val.as_deref()
                    .map(|v| NaiveDate::parse_from_str(v, "%Y%m%d").ok())
                    .flatten()
            });
            either_of::EitherOf3::C(view! {<DatePicker value/>})
        }
        _ => {
            let ph = match dtype {
                DataType::String => "请输入".to_owned(),
                DataType::Float => "请输入数值".to_owned(),
                _ => "请输入数值".to_owned(),
            };
            let value_change = on_value_change.clone();
            let allow_value = ArcOneCallback::<String, bool>::new(move |v: String| {
                if v.is_empty() {
                    value_change(index, None);
                    return true;
                }
                if dtype == DataType::Int {
                    let Ok(_v) = v.parse::<i64>() else {
                        return false;
                    };
                } else if dtype == DataType::Float {
                    let Ok(_v) = v.parse::<f64>() else {
                        return false;
                    };
                }
                value_change(index, Some(v.clone()));
                true
            });
            either_of::EitherOf3::B(
                view! {<Input value=value.get_value() allow_value placeholder=Some(ph)/>},
            )
        }
    };
    view! {
        <div class="flex flex-row gap-2">
            <div class="dropdown flex-none">
                <div tabindex="0" role="button" class="btn w-28">{oper.to_string()}</div>
                <ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52 max-h-40 overflow-auto">
                    {opers
                        .into_iter()
                        .map(|x| {
                            let sx = x.clone();
                            let operator_change = on_operator_change.clone();
                            view! {
                                <li><a on:click=move|_|operator_change(index,x.clone())>{sx.to_string()}</a></li>
                            }
                        })
                        .collect_view()}
                </ul>
            </div>
            <div class="flex-1">
                {control_view}
            </div>
            <div class="flex-none join items-center justify-center">
                {
                    move || if can_add.get().unwrap_or(false) {
                        let on_add_item = on_add_item.clone();
                        Some(view! {
                            <button class="btn btn-square btn-sm join-item" on:click=move |e: ev::MouseEvent| {
                                e.prevent_default();
                                on_add_item();
                            }>
                                <Icon icon=i::RiAddCircleSystemLine width="20" height="20"/>
                            </button>
                        })
                    }else{
                        None
                    }
                }
                {
                    move || if can_del.get().unwrap_or(false) {
                        let on_del_item = on_del_item.clone();
                        Some(view! {
                            <button class="flex-none btn btn-square btn-sm join-item" on:click=move |e: ev::MouseEvent| {
                                e.prevent_default();
                                on_del_item(index);
                            }>
                                <Icon icon=i::AiCloseCircleOutlined width="20" height="20"/>
                            </button>
                        })
                    }else{
                        None
                    }
                }
            </div>
        </div>
    }.into_any()
}
